package com.tianji.promotion.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.constants.BizTypeConstant;
import com.tianji.promotion.domain.dto.CouponFormDTO;
import com.tianji.promotion.domain.dto.CouponIssueFormDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.CouponQuery;
import com.tianji.promotion.domain.vo.CouponDetailVO;
import com.tianji.promotion.domain.vo.CouponPageVO;
import com.tianji.promotion.domain.vo.CouponScopeVO;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.enums.DiscountType;
import com.tianji.promotion.enums.ObtainType;
import com.tianji.promotion.enums.UserCouponStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author rui
 * @since 2024-08-05
 */
@Service
@RequiredArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {
    private final ICouponScopeService scopeService;
    private final IExchangeCodeService codeService;
    private final CategoryCache categoryCache;
    private final CourseClient courseClient;
    private final IUserCouponService userCouponService;
    @Override
    @Transactional
    public void addCoupon(Long id, CouponIssueFormDTO dto) {
        LocalDateTime now = LocalDateTime.now();
        //1.如果开始时间为空或者开始时间比现在时间早，则发放优惠券的方式是立即发放，否则是定时发放
        boolean isCurrent = dto.getIssueBeginTime() == null || !dto.getIssueBeginTime().isAfter(now);
        //2.如果isFixedDay为true，则发放优惠券的有效期是固定天数，否则是固定时间段
        boolean isFixedDay = dto.getTermDays() != null;
        //3..判断优惠券是否已经存在
        Coupon coupon = getById(dto.getId());
        if(coupon == null){
            //3.1 不存在则说明id错误
            throw new BadRequestException("请求参数id错误");
        }
        CouponStatus firstStatus = coupon.getStatus(); //保存第一次未发放前的优惠券状态，便于后面兑换码判断
        if(firstStatus != CouponStatus.DRAFT && firstStatus != CouponStatus.PAUSE){
            //3.2 不是暂停或未发放的状态
            throw new BizIllegalException("优惠券状态错误！");
        }

        //4.设置发放时间
        coupon.setIssueBeginTime(isCurrent?now : dto.getIssueBeginTime());
        coupon.setIssueEndTime(dto.getIssueEndTime());

        coupon.setTermDays(isFixedDay?dto.getTermDays():0);
        //4.1如果没有传TermBeginTime参数，则赋值null也行
        coupon.setTermBeginTime(dto.getTermBeginTime());
        coupon.setTermEndTime(dto.getTermEndTime());
        //4.2 设置优惠券状态，如果是立即发放，则设置为发放中，否则可能是定时发放，此时还未发放
        coupon.setStatus(isCurrent ? CouponStatus.ISSUING:CouponStatus.UN_ISSUE);
        //5.封装发放优惠券信息
        updateById(coupon);
        // 6.判断是否需要生成兑换码，优惠券类型必须是兑换码，优惠券状态必须是待发放
        if(coupon.getObtainWay() == ObtainType.ISSUE && firstStatus == CouponStatus.DRAFT){
            codeService.generateExchangeCode(coupon);
        }

    }

    @Override
    @Transactional //防止异常发生，能够进行回滚
    public void saveCoupon(CouponFormDTO formDTO) {
        //1.封装优惠券信息
        Coupon coupon = BeanUtil.toBean(formDTO, Coupon.class);
        if(coupon == null){
            throw new BizIllegalException("请求格式不对");
        }
        boolean save = save(coupon);
        if(!save){
            throw new DbException("保存优惠券失败");
        }
        //3.如果限定范围则需要保存优惠券的作用范围信息
        saveSpecific(formDTO, coupon);

    }

    @Override
    public PageDTO<CouponPageVO> pageQueryCoupon(CouponQuery query) {
        //1.分页查询记录
        Page<Coupon> page = lambdaQuery()
                .eq(query.getStatus() != null, Coupon::getStatus, query.getStatus())
                .eq(query.getType() != null, Coupon::getDiscountType, query.getType())
                .like(StrUtil.isNotBlank(query.getName()), Coupon::getName, query.getName())
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<Coupon> records = page.getRecords();
        if(records.isEmpty()){
            return PageDTO.empty(0L,0L);
        }
        List<CouponPageVO> couponPageVOS = BeanUtil.copyToList(records, CouponPageVO.class);
        return PageDTO.of(page,couponPageVOS);
    }

    @Override
    @Transactional
    public void updateCoupon(Long id,CouponFormDTO dto) {
        //1.查看对应id所在的数据是否存在
        Coupon coupon = getById(id);
        if(coupon == null || coupon.getStatus() != CouponStatus.DRAFT){
            throw new BadRequestException("请求参数错误");
        }
        //2.修改对应的数据
        Coupon CouponDB = BeanUtil.toBean(dto, Coupon.class);
        CouponDB.setId(coupon.getId());

        //3.数据库更新
        boolean success = updateById(CouponDB);
        if(success){
            saveSpecific(dto, coupon);
        }

    }

    @Override
    @Transactional
    public void deleteCoupon(Long id) {
        //1.判断删除的数据是否存在
        Coupon coupon = getById(id);
        //2.状态为待发放才能删除
        if(coupon.getStatus()!=CouponStatus.DRAFT){
            return;
        }
        boolean success = removeById(id);
        if(success){
            //3.删除对应的特定使用优惠券信息
            scopeService.remove(new LambdaQueryWrapper<CouponScope>()
                    .eq(CouponScope::getCouponId,id));
        }
    }

    @Override
    public CouponDetailVO queryCoupon(Long id) {
        //1.判断查找数据是否存在
        Coupon coupon = getById(id);
        if(coupon == null){
            throw new BadRequestException("非法请求");
        }
        CouponDetailVO couponDetailVO = BeanUtil.toBean(coupon, CouponDetailVO.class);
        //2.查看是否需要远程调用服务封装限定分类信息
        if(coupon.getSpecific()){
            Map<Integer, List<Long>> bizMap = scopeService.getSpecificByCouponId(id);
            List<CouponScopeVO> voList = new ArrayList<>();
            //2.1 如果分类为课程，则调用服务获取课程名称
            if(CollUtils.isNotEmpty(bizMap.get(BizTypeConstant.EXCHANGE_CODE_COURSE))){
                List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(bizMap.get(BizTypeConstant.EXCHANGE_CODE_COURSE));
                if(CollUtils.isNotEmpty(simpleInfoList)){
                    for (CourseSimpleInfoDTO infoDTO : simpleInfoList) {
                        CouponScopeVO vo = new CouponScopeVO();
                        vo.setId(infoDTO.getId());
                        vo.setName(infoDTO.getName());
                        voList.add(vo);
                    }
                }
            }
            //2.2 如果分类为大类，则调用缓存服务获取三级名称
            if(CollUtils.isNotEmpty(bizMap.get(BizTypeConstant.EXCHANGE_CODE_TYPE))){
                List<Long> bizIds = bizMap.get(BizTypeConstant.EXCHANGE_CODE_TYPE);
                for (Long bizId : bizIds) {
                    CouponScopeVO vo = new CouponScopeVO();
                    String nameByLv3Id = categoryCache.getNameByLv3Id(bizId);
                    if(nameByLv3Id!=null){
                        vo.setName(nameByLv3Id);
                        vo.setId(bizId);
                        voList.add(vo);
                    }
                }
            }
            couponDetailVO.setScopes(voList);
        }
        //3.封装返回信息
        return couponDetailVO;
    }

    @Override
    public void pauseCouponByStatus(Long id) {
        //1.查找优惠券在不在
        Coupon coupon = getById(id);
        if(coupon == null){
            throw new BadRequestException("非法请求");
        }
        //2.如果状态不是发放中则跳过
        if(!coupon.getStatus().equals(CouponStatus.ISSUING)){
            return;
        }
        //3.修改状态为暂停
        lambdaUpdate()
                .eq(Coupon::getId,id)
                .set(Coupon::getStatus,CouponStatus.PAUSE)
                .update();
    }

    @Override
    public List<CouponVO> queryUserCouponByList() {
        //1.优惠券的状态是要发放中，并且是手动领取
        List<Coupon> coupons = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.ISSUING)
                .eq(Coupon::getObtainWay, ObtainType.PUBLIC)
                .list();
        if(CollUtils.isEmpty(coupons)){
            return Collections.emptyList();
        }

        //1.1查看用户已经拥有的优惠券
        Long userId = UserContext.getUser();
        List<Long> couponIds = coupons.stream().map(Coupon::getId).collect(Collectors.toList());
        List<UserCoupon> userCoupons = userCouponService.lambdaQuery()
                .in(CollUtils.isNotEmpty(couponIds), UserCoupon::getCouponId, couponIds)
                .eq(UserCoupon::getUserId,userId)
                .list();
        //用户已经领取优惠券id：数量的map
        Map<Long, Long> issueMap = userCoupons.stream().collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));

        //用户已经领取还未使用优惠券的数量
        Map<Long, Long> unUsedMap = userCoupons.stream()
                .filter(c -> c.getStatus() == UserCouponStatus.UNUSED)
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        //2.封装coupons没有的参数
        List<CouponVO> voList = new ArrayList<>(coupons.size());
        for (Coupon coupon : coupons) {
            if(coupon!=null){
                CouponVO couponVO = BeanUtil.toBean(coupon,CouponVO.class);
                if(couponVO !=null){
                    Integer issueNum = coupon.getIssueNum();
                    Integer totalNum = coupon.getTotalNum();
                    Long num = issueMap.getOrDefault(coupon.getId(),0L);
                    //2.1 查看优惠券是否可领取（优惠券还有剩余，并且当前用户已领（包括未使用和已经使用）是否上限）
                    couponVO.setAvailable(issueNum < totalNum && num <coupon.getUserLimit());

                    //2.2 用户抢购的优惠券是否可使用,也就是未使用的优惠券数量是否大于0
                    Long unUsed = unUsedMap.getOrDefault(coupon.getId(), 0L);
                    couponVO.setReceived(unUsed > 0);
                    //2.3 添加进集合里
                    voList.add(couponVO);
                }
            }
        }
        return voList;
    }

    private void saveSpecific(CouponFormDTO dto, Coupon coupon) {
        if(!dto.getSpecific()){
            return;
        }
        List<Long> scopes = dto.getScopes();
        if(CollUtils.isNotEmpty(scopes)){
            List<CouponScope> couponScopes = new ArrayList<>(scopes.size());
            for (Long scope : scopes) {
                CouponScope couponScope = new CouponScope();
                if(scope != null){
                    couponScope.setCouponId(coupon.getId());
                    couponScope.setType(1);
                    couponScope.setBizId(scope);
                    couponScopes.add(couponScope);
                }
            }
            scopeService.saveBatch(couponScopes);
        }
    }
}
